package DesignMode;
//通过懒汉模式(不会很急切的吧类的实例创建出来,如果不用就不会创建出来了) 来实现 单列模式 并保证Singleton只能创建一个实例

/**   懒汉模式(前提是必须要满足只创建一个实例)并非是线程安全的(不满足期望的结果),
 *    原因: 当在多线程的环境下,不同的线程在调用getInstance()这个方法的时候,会进行lode , cmp 和new 以及save操作
 *    但是由于线程的随机调度,此时两个线程同时读到的instance都是null,那么就会new出两个不同的对象,就不满足单列模式下,只有一个实例的情况
 */
public class SingletonLazy {
    volatile private static SingletonLazy instance;   //volatile 有两个功能: 解决内存可见性 , 禁止指令重排序

    /**  问题一:  但是如果这样写那么每次调用getInstance() 都会进行加锁操作,而加锁是会造成开销的
     * 而代码中只是在第一次new对象的时候需要加锁,所以当对象还没创建就加锁,有了对象就不加锁
     *   问题二:  可能还会出现内存可见性问题: 一个线程修改了instance前,另一个线程一直处在锁竞争产生的阻塞状态很长时间,
     * 此时由于频繁读取同一个内存变量,导致这个线程之后读取的变量值都是在寄存器上的值(null),而且由于jvm再多线程情况下
     * 优化代码,即使第一个线程将instance变量修改,但是第二个线程仍然读取的是null(寄存器的值),此时就需要给变量加volatile
     * 保证每一次线程都是读取的内存上的值
     *   问题三:  在单线程环境下,此时不管是执行步骤123,还是执行步骤132 ,结果都是一样,但是在多线程环境系,
     * 会出现指令重排序的问题,假设有一个线程在进行new操做按照132执行,由于指令重排序,第一个线程执行到13后,执行2之前被切出cpu
     * 此时另一个线程来执行,当第一个线程执行完3后,*另外一个线程认为此处的引用不为空,此时此刻,这个线程相当于直接返回instance这个引用*
     * 但是由于第一个线程还没有执行完,此时另外一个线程就会出现获取到的是一个还没有构造完,不完整得对象,但是instance是非空的
     * 那么之后对这个对象的使用就会出现问题
     */
    public static SingletonLazy getInstance() {
        if(instance == null) {
            synchronized (SingletonLazy.class) {   //SingletonLazy.class 表示一个类的对象
                if(instance == null) {
                    /* new 操作分三个步骤: 1. 申请内存空间
                    *                   2. 调用构造方法,把这个内训空间初始化为一个合理的对象
                    *                   3. 把内存空间的地址赋给instance*/
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
    private SingletonLazy() {
    }
}
class Test1 {
    public static void main(String[] args) {
        SingletonLazy singletonLazy1 = SingletonLazy.getInstance();
        SingletonLazy singletonLazy2 = SingletonLazy.getInstance();
        System.out.println(singletonLazy1 == singletonLazy2);
    }
}